define(function(require, exports, module) {

  var Backbone = require('backbone');
  var $ = require('jquery');
  var _ = require('underscore');
  var jsonview = require('jsonview');
  var tmpl = require('text!../tmpl.html');

  var VSHADER_SOURCE = require('text!../shader/vertex.glsl')
  var FSHADER_SOURCE = require('text!../shader/fragment.glsl')

  var __ = require('hy5b/gl/index')
  var util = require('hy5b/math/index')

  var env = window.env

  var animation = require('hy5b/animation/index')

  function main() {
    var $app = $('<div id="app">');
    $('#main').html($app);

    $app.append(tmpl);

    var logger = p.getLogger();

    var elem = document.getElementById('webgl');

    var gl = getWebGLContext(elem);

    if (!gl) {
      logger("Your browser don't support webgl!!");
    }

    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
      logger("essl inited failed!!");
    }

    var size = 3

    function getVertexPoints(radius, tube, radialSegment, tubularSegment, arc) {
      radius = radius || 1;
      tube = tube || 0.4;
      var radialSegments = Math.floor( tubularSegment ) || 6;
      var tubularSegments = Math.floor( radialSegment ) || 8;
      arc = arc || Math.PI * 2;

      // buffers

      var indices = [];
      var vertices = [];
      var normals = [];
      var uvs = [];

      // helper variables

      // var center = new Vector3();
      var vertex = {x:0,y:0,z:0};
      // var normal = new Vector3();

      var j, i;

      // generate vertices, normals and uvs

      for ( j = 0; j <= radialSegments; j ++ ) {

        for ( i = 0; i <= tubularSegments; i ++ ) {

          var u = i / tubularSegments * arc;
          var v = j / radialSegments * Math.PI * 2;

          // vertex

          vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
          vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
          vertex.z = tube * Math.sin( v );

          vertices.push( [vertex.x, vertex.y, vertex.z] );

          // normal

          // center.x = radius * Math.cos( u );
          // center.y = radius * Math.sin( u );
          // normal.subVectors( vertex, center ).normalize();

          // normals.push( normal.x, normal.y, normal.z );

          // uv

          // uvs.push( i / tubularSegments );
          // uvs.push( j / radialSegments );

        }

      }

      // generate indices

      for ( j = 1; j <= radialSegments; j ++ ) {

        for ( i = 1; i <= tubularSegments; i ++ ) {

          // indices

          var a = ( tubularSegments + 1 ) * j + i - 1;
          var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
          var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
          var d = ( tubularSegments + 1 ) * j + i;

          // faces

          indices.push( [a, b, d] );
          indices.push( [b, c, d] );

        }

      }

      var k;
      var reflowVertexs = []
      for ( k = 0; k < indices.length; k ++ ) {
        reflowVertexs.push(
          vertices[indices[k][0]][0],vertices[indices[k][0]][1], vertices[indices[k][0]][2],
          vertices[indices[k][1]][0],vertices[indices[k][1]][1], vertices[indices[k][1]][2],
          vertices[indices[k][2]][0],vertices[indices[k][2]][1], vertices[indices[k][2]][2]
        )
      }

      return reflowVertexs;
    }

    gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
    gl.enable(gl.BLEND);
    gl.disable(gl.DEPTH_TEST);
    
    var projectionMatrix = gl.getUniformLocation(gl.program, 'projectionMatrix');

    var logicalW = elem.clientWidth
    var logicalH = elem.clientHeight
    var S = { //投影比例
      x: 1.0 / logicalW,
      y: 1.0 / logicalH,
      z: 1.0 / Math.max(logicalW, logicalH)
    }

    gl.uniformMatrix4fv(projectionMatrix, false,
      new Float32Array([
        S.x, 0.0, 0.0, 0.0,
        0.0, S.y, 0.0, 0.0,
        0.0, 0.0, S.z, 0.0,
        0.0, 0.0, 0.0, 1.0
      ])
    )

    var initVertexBuffers = function(radius, tube, radialSegment, tubularSegment) {

      var vertexPoints = getVertexPoints(radius, tube, radialSegment, tubularSegment)

      var n = vertexPoints.length / size;

      var vertexs = new Float32Array(vertexPoints);

      var vertexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, vertexs, gl.STATIC_DRAW);

      var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
      
      gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(a_Position);

      return n;
    }

    var initFragColor = function(color, opacity) {
      var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
      opacity = (opacity === undefined ? 1.0 : opacity);
      var g_colors = color || [0.1921568661928177, 0.21176470816135406, 0.5843137502670288, opacity];
      gl.uniform4f(u_FragColor, g_colors[0], g_colors[1], g_colors[2], g_colors[3]);
    }

    var u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix');

    function draw(angle, radius, opacity, tube, radialSegment, tubularSegment) {
      var n = initVertexBuffers(radius, tube, radialSegment, tubularSegment);
      initFragColor(null, opacity);

      // initFragColor([1.0, 1.0, 1.0, 1.0]);
      
      var cosB = util.cos(angle);
      var sinB = util.sin(angle);

      //旋转
      var xformMatrix = new Float32Array([
          // cosB, sinB, 0.0, 0.0,
          // -sinB, cosB, 0.0, 0.0,
          // 0.0, 0.0, 1.0, 0.0,
          // 0.0, 0.0, 0.0, 1.0 //绕Z

          1.0, 0.0, 0.0, 0.0,
          0.0, cosB, sinB, 0.0,
          0.0, -sinB, cosB, 0.0,
          0.0, 0.0, 0.0, 1.0 // 绕X

          // cosB, 0.0, sinB, 0.0,
          // 0.0, 1.0, 0.0, 0.0,
          // -sinB, 0.0, cosB, 0.0,
          // 0.0, 0.0, 0.0, 1.0 // 绕Y
      ]);
      gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
      
      // gl.drawArrays(gl.LINE_STRIP, 0, n);
    }

    var angle = 45.0;
    var radius = 60;
    var opacity = 1.0;

    function step() {
      clearGl(gl);
      requestAnimationFrame(step);
      angle++;
      draw(angle, 400, opacity, 150, 100, 20)
      // draw(angle, 400, opacity, 150, 800, 160)
    }

    if (1) {
      if (0) {
        step()
      } else {
        clearGl(gl);
        // draw(angle, 400, opacity, 150, 100, 20)
        draw(0, 60, opacity, 2, 100)
      }
    } else {
      var T = 1800;
      var duration = T;
      var delay = duration / 5;
      var animate = new animation({
        props: {
          radius: {
            start: 20,
            end: 100
          },
          opacity1: {
            start: 0,
            end: 1,
            duration: delay
          },
          opacity2: {
            start: 1,
            end: 0,
            duration: T - delay,
            delay: delay
          }
        },
        duration: duration,
        step: function(props) {
          clearGl(gl);
          drawAgent(props)
        }
      })

      function drawAgent(props) {
        var opacity = props.opacity1.stopped
          ? props.opacity2.now
          : props.opacity1.now

        draw(angle, props.radius.now, opacity, 2, 100)
      }

      // animate.start()

      var t = 0;
      var betweenTime = T / 3;
      function step() {
        requestAnimationFrame(step)
        t += env.FRAME60;

        clearGl(gl);
        var props;
        for (var i = 0; i < 3; i++) {
          props = animate.getStaticFrame((t + betweenTime * i) % T);
          drawAgent(props);
        }
      }

      step()

    }
    
  }

  function myNormalize(p, normalX) {
    p.x *= 1 / normalX
    p.y *= 1 / normalX
    p.z *= 1 / normalX
  }

  function normalize(p) {
    var length = Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z) || 1
    p.x *= 1 / length
    p.y *= 1 / length
    p.z *= 1 / length
  }

  return main;
})
